import cv2
import numpy as np
import math
import win32con
import win32api
import time

capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)


class KeyEvent():
    def action(self, num):
        if num == 2:
            self.move_left_key()
        elif num == 3:
            self.move_right_key()
        elif num == 4:
            self.shooting_key()

    def shooting_key(self):
        win32api.keybd_event(38, 0, 0, 0)  # 键盘按下上键 38上键
        time.sleep(0.1)
        win32api.keybd_event(38, 0, win32con.KEYEVENTF_KEYUP, 0)

    def move_left_key(self):
        win32api.keybd_event(37, 0, 0, 0)  # 键盘按下左键 37左键
        time.sleep(0.1)
        win32api.keybd_event(37, 0, win32con.KEYEVENTF_KEYUP, 0)

    def move_right_key(self):
        win32api.keybd_event(39, 0, 0, 0)  # 键盘按下右键 39下键
        time.sleep(0.1)
        win32api.keybd_event(39, 0, win32con.KEYEVENTF_KEYUP, 0)


while (capture.isOpened()):
    ret, frame = capture.read()  # 读取摄像头每帧图片

    frame = cv2.flip(frame, 1)
    kernel = np.ones((2, 2), np.uint8)
    roi = frame[0:250, 0:250]  # 选取图片中固定位置作为手势输入

    cv2.rectangle(frame, (0, 0), (250, 250), (0, 0, 255), 0)  # 用红线画出手势识别框
    # 基于hsv的肤色检测
    hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    lower_skin = np.array([0, 28, 70], dtype=np.uint8)
    upper_skin = np.array([20, 255, 255], dtype=np.uint8)

    # 进行高斯滤波
    mask = cv2.inRange(hsv, lower_skin, upper_skin)
    mask = cv2.dilate(mask, kernel, iterations=4)
    mask = cv2.GaussianBlur(mask, (5, 5), 100)

    # 找出轮廓
    contours, h = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    # 按照面积排序
    cnt = max(contours, key=lambda x: cv2.contourArea(x))
    # 计算轮廓的长度并*0.0005作为阈值返回
    epsilon = 0.0005 * cv2.arcLength(cnt, True)
    # 寻找逼近轮廓的多边形，epsilon作为逼近的精度
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    # 凸包检测
    hull = cv2.convexHull(cnt)
    # 求出凸包的面积
    areahull = cv2.contourArea(hull)
    # 轮廓总面积
    areacnt = cv2.contourArea(cnt)
    # 求出比率，用于下一步作为判断值
    arearatio = ((areahull - areacnt) / areacnt) * 100
    # 求出凹凸点
    hull = cv2.convexHull(approx, returnPoints=False)
    defects = cv2.convexityDefects(approx, hull)
    l = 0  # 定义凹凸点个数初始值为0
    for i in range(defects.shape[0]):
        s, e, f, d, = defects[i, 0]
        start = tuple(approx[s][0])
        end = tuple(approx[e][0])
        far = tuple(approx[f][0])
        pt = (100, 100)

        a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
        b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
        c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
        s = (a + b + c) / 2
        ar = math.sqrt(s * (s - a) * (s - b) * (s - c))
        # 手指间角度求取
        angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) * 57

        if angle <= 90 and d > 20:
            l += 1
            cv2.circle(roi, far, 3, [255, 0, 0], -1)
        cv2.line(roi, start, end, [0, 255, 0], 2)  # 画出包络线
    l += 1
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(frame, "2 lift 3right 4shoot esc exit", (0,300), font, 2, (255,255, 255), 1, cv2.LINE_AA)
    cv2.putText(frame, "esc exit", (0, 400), font, 2, (255, 255, 255), 1, cv2.LINE_AA)
    # 识别到2，左移，识别到3，右移，识别到4，发射
    w = KeyEvent()
    w.action(l)
    # 将识别到的数字在视频中展示
    if l == 1:
        if areacnt < 2000:
            cv2.putText(frame, "put hand in the window", (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
        else:
            if arearatio < 12:
                cv2.putText(frame, '0', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
            elif arearatio < 17.5:
                cv2.putText(frame, "1", (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
            else:
                cv2.putText(frame, '1', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
    elif l == 2:
        cv2.putText(frame, '2', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
    elif l == 3:
        if arearatio < 27:
            cv2.putText(frame, '3', (0, 50), font, 2,(255,255, 255), 3, cv2.LINE_AA)
        else:
            cv2.putText(frame, '3', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
    elif l == 4:
        cv2.putText(frame, '4', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
    elif l == 5:
        cv2.putText(frame, '5', (0, 50), font, 2, (255,255, 255), 3, cv2.LINE_AA)
    cv2.imshow('opencv_ctrl', frame)
    cv2.imshow('mask', mask)
    k = cv2.waitKey(25) & 0xff
    if k == 27:  # 键盘Esc键退出
        break
cv2.destroyAllWindows()
capture.release()
